home *** CD-ROM | disk | FTP | other *** search
- unit Format;
-
- interface
-
- uses
- SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
- Forms, Dialogs;
-
- type
- ProgressHook = procedure (percent: Integer);
-
- const
- { Format capacities }
- DF_360K = 0;
- DF_12M = 1;
- DF_720K = 2;
- DF_14M = 3;
- DF_28M = 4;
- DF_Unknown = 5;
-
- fAbort: Bool = False; { in interface so that host can tweak it }
- Progress: ProgressHook = Nil; { ditto }
-
- function IsWindows95: Bool;
- function FormatDisk (Drive, Size: Integer): Integer;
-
- implementation
-
- type
- PBPB = ^BPB;
- BPB = record { offsets assume boot sector }
- bsBytesPerSec: Integer; { 00 bytes per sector }
- bsSecPerClust: Byte; { 02 sectors per cluster }
- bsResSectors: Integer; { 03 number of reserved sectors }
- bsFATs: Byte; { 05 number of file allocation tables }
- bsRootDirEnts: Integer; { 06 number of root-directory entries }
- bsSectors: Integer; { 08 total number of sectors }
- bsMedia: Byte; { 0A media descriptor }
- bsFATsecs: Integer; { 0B number of sectors per FAT }
- bsSecPerTrack: Integer; { 0D sectors per track }
- bsHeads: Integer; { 0F number of heads }
- bsHidden1: Integer; { 11 hidden sectors (lo) }
- end;
-
- PDeviceParams = ^DeviceParams;
- DeviceParams = record
- SpecFunc: Byte; { 00 }
- DevType: Byte; { 01 }
- DevAttrs: Integer; { 02 }
- Tracks: Integer; { 04 }
- MediaType: Byte; { 06 }
- bpb: BPB; { 07 }
- bsHidden2: Integer; { 1A }
- HugeSectors: LongInt; { 1C }
- Reserved: array [0..5] of Char; { 20 !!! UNDOCUMENTED !!!}
- { Start of TRACKLAYOUT information }
- SectorsPerTrack: Integer; { 26 }
- TrackLayout: array [0..35] of LongInt; { 28 }
- end;
-
- PDiskType = ^DiskType;
- DiskType = record
- spc: Byte; { sectors per cluster }
- rde: Integer; { number of root-dir entries }
- sec: Integer; { total number of sectors }
- med: Byte; { media descriptor }
- spf: Integer; { number of sectors per FAT }
- spt: Integer; { sectors per track }
- cls: Integer; { cluster count }
- end;
-
- RWBlock = record
- rwSpecFunc: Byte; { special functions (must be zero) }
- rwHead: Integer; { head to read/write }
- rwCylinder: Integer; { cylinder to read/write }
- rwFirstSector: Integer; { first sector to read/write }
- rwSectors: Integer; { number of sectors to read/write }
- rwBuffer: Pointer; { address of buffer for read/write data }
- end;
-
- const
- { This array maps a logical drive type to a list of }
- { parameters for that drive. Assumptions: }
- { Bytes per sector = 512 Reserved sectors = 1 }
- { Number of FATS = 2 Heads = 2 }
- { Hidden sectors = 0 Tracks = 80 except 40 for 1st }
-
- DiskTypes: array [0..4] of DiskType = (
-
- (spc:2; rde:112; sec: 720; med:$FD; spf:2; spt: 9; cls:354), { 360 K }
- (spc:1; rde:224; sec:2400; med:$F9; spf:7; spt:15; cls:2371), { 1.2 M }
- (spc:2; rde:112; sec:1440; med:$F9; spf:3; spt: 9; cls:713), { 720 K }
- (spc:1; rde:224; sec:2880; med:$F0; spf:9; spt:18; cls:2847), { 1.4 M }
- (spc:2; rde:240; sec:5760; med:$F0; spf:9; spt:36; cls:2863)); { 2.8 M }
-
-
- const FloppyBoot: array [0..511] of Byte = (
- $EB, $3C, $90, $4D, $53, $44, $4F, $53,
- $35, $2E, $30, $00, $02, $01, $01, $00,
- $02, $E0, $00, $40, $0B, $F0, $09, $00,
- $12, $00, $02, $00, $00, $00, $00, $00,
- $00, $00, $00, $00, $00, $00, $29, $E9,
- $17, $47, $37, $4E, $4F, $20, $4E, $41,
- $4D, $45, $20, $20, $20, $20, $46, $41,
- $54, $31, $32, $20, $20, $20, $FA, $33,
- $C0, $8E, $D0, $BC, $00, $7C, $16, $07,
- $BB, $78, $00, $36, $C5, $37, $1E, $56,
- $16, $53, $BF, $3E, $7C, $B9, $0B, $00,
- $FC, $F3, $A4, $06, $1F, $C6, $45, $FE,
- $0F, $8B, $0E, $18, $7C, $88, $4D, $F9,
- $89, $47, $02, $C7, $07, $3E, $7C, $FB,
- $CD, $13, $72, $79, $33, $C0, $39, $06,
- $13, $7C, $74, $08, $8B, $0E, $13, $7C,
- $89, $0E, $20, $7C, $A0, $10, $7C, $F7,
- $26, $16, $7C, $03, $06, $1C, $7C, $13,
- $16, $1E, $7C, $03, $06, $0E, $7C, $83,
- $D2, $00, $A3, $50, $7C, $89, $16, $52,
- $7C, $A3, $49, $7C, $89, $16, $4B, $7C,
- $B8, $20, $00, $F7, $26, $11, $7C, $8B,
- $1E, $0B, $7C, $03, $C3, $48, $F7, $F3,
- $01, $06, $49, $7C, $83, $16, $4B, $7C,
- $00, $BB, $00, $05, $8B, $16, $52, $7C,
- $A1, $50, $7C, $E8, $92, $00, $72, $1D,
- $B0, $01, $E8, $AC, $00, $72, $16, $8B,
- $FB, $B9, $0B, $00, $BE, $E6, $7D, $F3,
- $A6, $75, $0A, $8D, $7F, $20, $B9, $0B,
- $00, $F3, $A6, $74, $18, $BE, $9E, $7D,
- $E8, $5F, $00, $33, $C0, $CD, $16, $5E,
- $1F, $8F, $04, $8F, $44, $02, $CD, $19,
- $58, $58, $58, $EB, $E8, $8B, $47, $1A,
- $48, $48, $8A, $1E, $0D, $7C, $32, $FF,
- $F7, $E3, $03, $06, $49, $7C, $13, $16,
- $4B, $7C, $BB, $00, $07, $B9, $03, $00,
- $50, $52, $51, $E8, $3A, $00, $72, $D8,
- $B0, $01, $E8, $54, $00, $59, $5A, $58,
- $72, $BB, $05, $01, $00, $83, $D2, $00,
- $03, $1E, $0B, $7C, $E2, $E2, $8A, $2E,
- $15, $7C, $8A, $16, $24, $7C, $8B, $1E,
- $49, $7C, $A1, $4B, $7C, $EA, $00, $00,
- $70, $00, $AC, $0A, $C0, $74, $29, $B4,
- $0E, $BB, $07, $00, $CD, $10, $EB, $F2,
- $3B, $16, $18, $7C, $73, $19, $F7, $36,
- $18, $7C, $FE, $C2, $88, $16, $4F, $7C,
- $33, $D2, $F7, $36, $1A, $7C, $88, $16,
- $25, $7C, $A3, $4D, $7C, $F8, $C3, $F9,
- $C3, $B4, $02, $8B, $16, $4D, $7C, $B1,
- $06, $D2, $E6, $0A, $36, $4F, $7C, $8B,
- $CA, $86, $E9, $8A, $16, $24, $7C, $8A,
- $36, $25, $7C, $CD, $13, $C3, $0D, $0A,
- $4E, $6F, $6E, $2D, $53, $79, $73, $74,
- $65, $6D, $20, $64, $69, $73, $6B, $20,
- $6F, $72, $20, $64, $69, $73, $6B, $20,
- $65, $72, $72, $6F, $72, $0D, $0A, $52,
- $65, $70, $6C, $61, $63, $65, $20, $61,
- $6E, $64, $20, $70, $72, $65, $73, $73,
- $20, $61, $6E, $79, $20, $6B, $65, $79,
- $20, $77, $68, $65, $6E, $20, $72, $65,
- $61, $64, $79, $0D, $0A, $00, $49, $4F,
- $20, $20, $20, $20, $20, $20, $53, $59,
- $53, $4D, $53, $44, $4F, $53, $20, $20,
- $20, $53, $59, $53, $00, $00, $55, $AA );
-
- const
- DiskParams: PChar = Nil; { pointer to disk params }
-
- var
- dp: DeviceParams;
- TargetBPB: BPB;
- OldDeviceParams: DeviceParams; { stash for drive params }
- OldDiskParams: array [0..10] of Char; { stash for old values }
-
- function LUVolumePrim (Drive, Level, Op: Byte; Perm: Word): Integer; assembler;
- asm
- mov ax,$440D { specify generic IOCTL call }
- mov bl,Drive { get drive number in BL }
- dec bl { for compatability, A: = 1 }
- mov bh,Level { get lock level in BH }
- mov ch,8 { category 8 for drives }
- mov cl,Op { get lock/unlock physical }
- mov dx,Perm { get permissions word }
- int 21h { make the call }
- jc @@1 { branch if error }
- xor ax,ax { no error - so AX = 0 }
- @@1:
- end;
-
- function LockVolume (Drive: Byte): Integer;
- begin
- if IsWindows95 then
- begin
- LockVolume := -1;
- if LUVolumePrim (Drive, 0, $4B, 0) = 0 then
- if LUVolumePrim (Drive, 0, $4B, 4) = 0 then
- LockVolume := 0;
- end
- else LockVolume := 0;
- end;
-
- function UnLockVolume (Drive: Byte): Integer;
- begin
- if IsWindows95 then
- begin
- UnLockVolume := -1;
- if LUVolumePrim (Drive, 0, $6B, 0) = 0 then
- if LUVolumePrim (Drive, 0, $6B, 0) = 0 then
- UnLockVolume := 0;
- end
- else UnLockVolume := 0;
- end;
-
- { Determine if we're running Windows 95 (or later) }
-
- function IsWindows95: Bool;
- var
- ver: LongInt;
- v: array [0..1] of Word absolute ver;
- begin
- ver := GetVersion;
- IsWindows95 := (Swap (v[0]) >= $35F) and (v[1] >= $700);
- end;
-
- function SenseMediaType (Drive: Byte; fDefault: Bool; pdp: PDeviceParams): Integer;
- var
- count: Integer;
- begin
- FillChar (pdp^, sizeof (DeviceParams), 0);
- if not fDefault then pdp^.SpecFunc := 1;
-
- asm
- mov ax,440Dh { Specify generic IOCTL call }
- mov bl,Drive { BL = wanted drive number }
- mov cx,$0860 { Request device parameters }
- push ds { save DS register }
- lds dx,pdp { ds:dx points to param block }
- call Dos3Call { make the call }
- pop ds { restore DS register }
- jc @@1 { if error, return code in AX }
- xor ax,ax { else clear AX register }
- @@1:
- mov count,ax { stash result in 'err' }
- end;
-
- if count <> 0 then SenseMediaType := -1 else
- for count := DF_360K to DF_28M do
- if pdp^.bpb.bsSectors = DiskTypes [count].sec then
- begin
- SenseMediaType := count;
- Exit;
- end;
- end;
-
- procedure FormatInit;
- begin
- if DiskParams = Nil then
- begin
- { Reset disk system and get INT $1E vector }
- asm
- mov ah,$0D { specify disk reset }
- call Dos3Call { do it }
- mov ax,$351E { specify INT $1E vector }
- call DOS3Call { result in ES:BX regs }
- mov word ptr DiskParams,bx { set up offset part }
- mov word ptr DiskParams+2,es { set up segment part }
- end;
-
- { Make a copy of existing disk parameters }
- Move (DiskParams^, OldDiskParams, sizeof (OldDiskParams));
- end;
- end;
-
- procedure FormatTerminate;
- begin
- if DiskParams <> Nil then
- begin
- { Restore old disk parameter values }
- Move (OldDiskParams, DiskParams^, sizeof (OldDiskParams));
- DiskParams := Nil;
- end;
- end;
-
- function GenSerialNumber: LongInt; assembler;
- asm
- mov ah,$2A { request system date }
- call DOS3Call { result in CX:DX }
- push cx { push year part }
- push dx { push month/day }
- mov ah,$2C { request system time }
- call DOS3Call { result in CX:DX }
- pop ax { pop month/day }
- add ax,dx { add to seconds/100 }
- pop dx { pop year part }
- add dx,cx { add hours/minutes }
- end;
-
- function WriteAbs (buff: Pointer; Drive, Track, Head, Sec: Integer): Integer;
- var
- p: Pointer;
- err: Integer;
- rwb: RWBLOCK;
- begin
- rwb.rwSpecFunc := 0; { always zero }
- rwb.rwHead := Head; { head to read/write }
- rwb.rwCylinder := Track; { track to read/write }
- rwb.rwFirstSector := Sec; { first sector to read/write }
- rwb.rwSectors := 1; { # sectors to read/write }
- rwb.rwBuffer := buff; { buffer for data }
-
- p := @rwb;
- err := 0;
-
- asm
- mov ax,$440D { specify generic IOCTL call }
- mov bl,byte ptr Drive { BL = drive number }
- mov cx,$0841 { write track to disk }
- push ds { save DS on stack }
- lds dx,p { point to param block }
- call DOS3Call { do the business... }
- pop ds { restore DS register }
- jnc @@1 { branch if no error }
- xor bx,bx { clear BX register }
- mov ah,$59 { request extended error code}
- call DOS3Call { result in AX register }
- mov err,ax { stash it }
- @@1:
- end;
-
- WriteAbs := err;
- end;
-
- function WriteBootSector (Drive: Byte; SrcBPB: PBPB): Integer;
- const
- BPBSig: array [0..7] of Char = 'FAT12 ';
- var
- DestBPB: PBPB;
- i: Integer;
- SerNum: LongInt;
- BootSector: array [0..511] of Byte;
- begin
- { Get a copy of the default boot record }
- Move (FloppyBoot, BootSector, sizeof (BootSector));
- { Add the BPB for this specific disk capacity }
- DestBPB := @BootSector [11];
- DestBPB^ := SrcBPB^;
- { Init extended boot stuff }
- for i := $1E to $24 do BootSector [i] := 0;
- SerNum := GenSerialNumber;
- Move (SerNum, BootSector [$27], sizeof (SerNum));
- Move (BPBSig, BootSector [$36], 8);
- WriteBootSector := WriteAbs (@BootSector, Drive, 0, 0, 0);
- end;
-
- function SetMediaType (Drive, Size: Integer): Integer;
- var
- err: Byte;
- p: Pointer;
- sec: Integer;
- dp: DeviceParams;
- begin
- { Use default diskparams as starting point }
- dp := OldDeviceParams;
- if Size = -1 then dp.SpecFunc := 4 else
- begin
- { Set up 'dp' according to wanted disk size }
- dp.SpecFunc := 5;
- dp.DevType := Size;
- if Size = 3 then dp.DevType := 7;
- if Size = 4 then dp.DevType := 9;
- if Size = 0 then begin dp.Tracks := 40; dp.MediaType := 1; end;
-
- dp.bpb.bsBytesPerSec := 512;
- dp.bpb.bsSecPerClust := DiskTypes [Size].spc;
- dp.bpb.bsResSectors := 1;
- dp.bpb.bsFATs := 2;
- dp.bpb.bsRootDirEnts := DiskTypes [Size].rde;
- dp.bpb.bsSectors := DiskTypes [Size].sec;
- dp.bpb.bsMedia := DiskTypes [Size].med;
- dp.bpb.bsFATsecs := DiskTypes [Size].spf;
- dp.bpb.bsSecPerTrack := DiskTypes [Size].spt;
- dp.bpb.bsHeads := 2;
- dp.bpb.bsHidden1 := 0;
-
- TargetBPB := dp.bpb;
- dp.bsHidden2 := 0;
- dp.HugeSectors := 0;
- dp.SectorsPerTrack := dp.bpb.bsSecPerTrack;
- for sec := 0 to dp.SectorsPerTrack - 1 do
- dp.TrackLayout [sec] := MakeLong (sec + 1, 512);
- end;
-
- { Now tell DOS this is what we want ! }
- p := @dp;
- err := 0;
-
- asm
- mov ax,$440D { specify generic IOCTL call }
- mov bl,byte ptr Drive { BL = drive number }
- mov cx,$0840 { set device parameters }
- push ds { save DS on stack }
- lds dx,p { get pointer to ParamBlock }
- call DOS3Call { do the business... }
- pop ds { restore DS register }
- jnc @@1 { branch if no error }
- mov err,ah { stash error code }
- @@1:
- end;
-
- SetMediaType := err;
- end;
-
- function FormatTrack (Drive, Track, Head: Byte): Integer;
- type
- FVBlock = record
- SpecFunc: Byte;
- fvHead: Integer;
- fvCylinder: Integer;
- fvTracks: Integer;
- end;
- var
- err: Integer;
- p: Pointer;
- fvb: FVBlock;
- begin
- fvb.SpecFunc := 0;
- fvb.fvHead := Head;
- fvb.fvCylinder := Track;
-
- p := @fvb;
- err := 0;
-
- asm
- mov ax,$440D { specify generic IOCTL call }
- mov bl,Drive { BL = drive number }
- mov cx,$0842 { format track on drive }
- push ds { save DS on stack }
- lds dx,p { point to Format block }
- call DOS3Call { format the track... }
- pop ds { restore DS register }
- jnc @@1 { branch if no error }
- xor bx,bx { clear BX register }
- mov ah,$59 { request extended error code}
- call DOS3Call { result in AX register }
- mov err,ax { stash it }
- @@1:
- end;
-
- if not (err in [0, $17, $1B, $1F]) then err := -1;
- FormatTrack := err;
- end;
-
- function InitVolume (Drive: Integer; pDisk: PDiskType): Integer;
- var
- buff: array [0..511] of Byte;
- count1, count2, trk, sec, hed: Integer;
-
- function PutSector: Integer;
- var
- err: Integer;
- begin
- err := WriteAbs (@buff, Drive, trk, hed, sec);
-
- Inc (sec);
- { End of this track ? }
- if sec > pDisk^.spt - 1 then
- begin
- sec := 0;
- Inc (hed);
- { End of this cylinder ? }
- if hed > 1 then
- begin
- hed := 0;
- Inc (trk);
- end;
- end;
-
- FillChar (buff, sizeof (buff), 0);
- PutSector := err;
- end;
-
- begin
- InitVolume := -1; { assume failure }
- trk := 0; hed := 0; sec := 1; { point at first FAT }
-
- { Write first and second FAT's to disk }
- for count1 := 1 to 2 do
- begin
- FillChar (buff, sizeof (buff), 0);
- buff [0] := pDisk^.med;
- buff [1] := $ff;
- buff [2] := $ff;
-
- for count2 := 1 to pDisk^.spf do
- if PutSector <> 0 then Exit;
- end;
-
- { Write empty directory blocks to disk }
- count1 := ((pDisk^.rde * $20) + 511) div 512;
- for count2 := 1 to count1 do
- if PutSector <> 0 then Exit;
- InitVolume := 0;
- end;
-
- function FormatDisk (Drive, Size: Integer): Integer;
- label
- Stop;
- var
- pDisk: PDiskType;
- TracksLeft, TotTracks, CurTrk, CurHead,
- err, SysSectors, DefSize, DiskSize: Integer;
-
- function Spin: Bool;
- begin
- Application.ProcessMessages;
- if Assigned (Progress) then Progress (CurTrk * 200 div TotTracks);
- Spin := not fAbort;
- end;
-
- begin
- { Assume failure and validate drive number }
- FormatDisk := -1;
- fAbort := False;
- if not Drive in [1..2] then Exit;
-
- { Stash current drive setup }
- DefSize := SenseMediaType (Drive, True, @OldDeviceParams);
-
- { If we're quick-formatting, then auto-sense the current media }
- DiskSize := Size;
- if DiskSize = -1 then DiskSize := SenseMediaType (Drive, False, @dp);
-
- { If media not present or other error, then slow-format }
- if DiskSize = -1 then
- begin
- if MessageDlg ('Can''t quick-format this disk. Format to default capacity?',
- mtConfirmation, [mbYes, mbNo], 0) = mrNo then Exit;
- DiskSize := DefSize;
- end;
-
- { Establish wanted media size with DOS }
- if SetMediaType (Drive, DiskSize) <> 0 then Exit;
-
- { Grab disk params table }
- pDisk := @DiskTypes [DiskSize];
- if LockVolume (Drive) <> 0 then Exit;
- FormatInit;
-
- { Tweak disk params table for wanted format }
- DiskParams [4] := Chr (pDisk^.spt);
- if pDisk^.spt = 15 then DiskParams [7] := Chr ($54)
- else DiskParams [7] := Chr ($50);
-
- { Now we can format the tracks }
- if DiskSize = 0 then TotTracks := 80 else TotTracks := 160; { Heads=2! }
- SysSectors := (2 * pDisk^.spf) + (((pDisk^.rde * 32) + 511) div 512) + 1;
- TracksLeft := TotTracks; CurHead := 0; CurTrk := 0;
-
- { Only format tracks if not quick formatting }
- if Size <> -1 then
- begin
- { Main formatting loop }
- while TracksLeft <> 0 do
- begin
- { Let somebody else get a look-in ! }
- if not Spin then goto Stop;
- if FormatTrack (Drive, CurTrk, CurHead) <> 0 then goto Stop;
-
- Dec (TracksLeft);
- Inc (CurHead);
- if CurHead >= 2 then
- begin
- CurHead := 0;
- Inc (CurTrk);
- end;
- end;
- end;
-
- { Write a new boot sector to the disk }
- WriteBootSector (Drive, @TargetBPB);
- { Let somebody else get a look-in ! }
- if not Spin then goto Stop;
- { Write FAT and directory information }
- if InitVolume (Drive, pDisk) = 0 then FormatDisk := 0;
- { If quick formatting, be sure to update Progress marker }
- if (Size = -1) and Assigned (Progress) then Progress (100);
-
- Stop:
- SetMediaType (Drive, -1);
- FormatTerminate;
- UnlockVolume (Drive);
- end;
-
- end.
-